package com.jiyongjun.component.core.impl;

import com.jiyongjun.component.annotation.MethodMetadata;
import com.jiyongjun.component.config.LogSwitch;
import com.jiyongjun.component.core.model.MethodLogContext;
import com.jiyongjun.component.exception.BizException;
import com.jiyongjun.component.exception.ServiceException;
import com.jiyongjun.component.utils.LogContextUtils;
import com.jiyongjun.component.utils.ParamValidator;
import com.jiyongjun.component.utils.SpringElExpressionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;

import static com.jiyongjun.component.enums.LogConstant.LOG_EXCEPTION_KEYWORD;

/**
 * <h2></h2>
 * <h3>描述：</h3>
 *
 * @author yongjun.jyj
 * @since 2022/7/14 20:12
 */
@Aspect
@Order
@Slf4j
public class MethodWatchDogAspect {
    @Around(value = "@annotation(methodMetadata)")
    public Object around(ProceedingJoinPoint pjp, MethodMetadata methodMetadata) throws Throwable{
        MethodSignature signature = (MethodSignature) pjp.getSignature();

        String methodUniqueName = LogContextUtils.generateMethodUniqueName(signature.getMethod());
        MethodLogContext logContext = MethodLogContext.getLogContext(methodUniqueName)
                .setApiDesc(methodMetadata.desc())
                .setApiName(LogContextUtils.generateMethodSimpleName(signature.getMethod()))
                .setMethod(signature.getMethod());

        if (methodMetadata.logArgs()) {
            logContext.setArgs(pjp.getArgs());
        }

        if (StringUtils.isNotBlank(methodMetadata.bizIdExpression())) {
            logContext.setBizId(parseBizId(pjp, methodMetadata));
        }

        // 校验参数
        if (methodMetadata.autoValidateArgs() && pjp.getArgs() != null) {
            try {
                ParamValidator.validateParameters(pjp.getTarget(), signature.getMethod(), pjp.getArgs());
            } catch (BizException e) {
                logContext.setBizException(e.getMsgCode(), e.getMessage(), e).close();
                throw e;
            } catch (Exception e) {
                log.error("{}|记录method日志异常|msg={}", LOG_EXCEPTION_KEYWORD, e.getMessage(), e);
            }
        }

        // 执行目标方法
        try {
            Object result = pjp.proceed();

            if (methodMetadata.logResult() && !LogSwitch.LOG_RESULT_API_BLACKLIST.contains(logContext.getApiUniqueName())) {
                logContext.setResult(result);
            }

            return result;
        } catch (BizException e) {
            logContext.setBizException(e);
            throw e;
        } catch (IllegalArgumentException e) {
            logContext.setBizException(null, e.getMessage(), e);
            throw e;
        } catch (ServiceException e) {
            logContext.setServiceException(e);
            throw e;
        } catch (Throwable throwable) {
            logContext.setException(throwable);
            throw throwable;
        } finally {
            logContext.close();
        }
    }

    private String parseBizId(ProceedingJoinPoint pjp, MethodMetadata methodMetadata) {
        if (methodMetadata.bizIdExpression().isEmpty()) {
            return null;
        }
        try {
            return SpringElExpressionUtil.parseElExpression(pjp.getArgs(),
                    (((MethodSignature) pjp.getSignature()).getMethod()), methodMetadata.bizIdExpression(), String.class);
        } catch (Exception e) {
            log.warn("{}|解析业务id异常|msg={}", LOG_EXCEPTION_KEYWORD, e.getMessage(), e);
            return null;
        }
    }
}
